/**
 * Project: yui3-common-mybatisx-base
 * Class LambdaFindWrapper
 * Version 1.0
 * File Created at 2020-12-1
 * $Id$
 * author yuyi
 * email 1060771195@qq.com
 */
package yui.comn.mybatisx.core.conditions.query;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.SharedString;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;

import yui.comn.mybatisx.core.conditions.AbstractLambdaWrapper;
import yui.comn.mybatisx.core.toolkit.TableUtils;

/**
 * <p>
 * Lambda 语法使用 Wrapper
 * </p>
 *
 * @author yuyi (1060771195@qq.com)
 */
public class LambdaFindWrapper<T> extends AbstractLambdaWrapper<T, LambdaFindWrapper<T>>
        implements Find<LambdaFindWrapper<T>, T, SFunction<T, ?>> {
    private static final long serialVersionUID = 1343461970627714897L;
    /**
     * 查询字段
     */
    private SharedString sqlSelect = new SharedString();

    /**
     * 不建议直接 new 该实例，使用 Wrappers.lambdaQuery(entity)
     */
    public LambdaFindWrapper() {
        this((T) null);
    }
    
    /**
     * 不建议直接 new 该实例，使用 Wrappers.lambdaQuery(entity)
     */
    public LambdaFindWrapper(T entity) {
        super.setEntity(entity);
        super.initNeed();
    }
    
    /**
     * 不建议直接 new 该实例，使用 Wrappers.lambdaQuery(entity)
     */
    public LambdaFindWrapper(Class<T> entityClass) {
        super.setEntityClass(entityClass);
        super.initNeed();
    }
    
    /**
     * 不建议直接 new 该实例，使用 Wrappers.lambdaQuery(...)
     */
    LambdaFindWrapper(T entity, Class<T> entityClass, SharedString sqlSelect, AtomicInteger paramNameSeq,
            Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias,
            SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {
    	super.setEntity(entity);
        super.setEntityClass(entityClass);
        this.paramNameSeq = paramNameSeq;
        this.paramNameValuePairs = paramNameValuePairs;
        this.expression = mergeSegments;
        this.sqlSelect = sqlSelect;
        this.paramAlias = paramAlias;
        this.lastSql = lastSql;
        this.sqlComment = sqlComment;
        this.sqlFirst = sqlFirst;
    }

    
    @SafeVarargs
    @Override
    public final LambdaFindWrapper<T> select(SFunction<T, ?>... columns) {
        return select(getEntityClass(), columns);
    }

    @SafeVarargs
    @Override
    public final LambdaFindWrapper<T> select(Class<?> clazz, SFunction<T, ?>... columns) {
        if (ArrayUtils.isNotEmpty(columns)) {
            if (null == clazz) {
                clazz = getEntityClass();
            }
            if (StringUtils.isBlank(this.sqlSelect.getStringValue())) {
                this.sqlSelect.setStringValue(TableUtils.sqlSelect(clazz, columnsToString2(false, columns)));
            } else {
                this.sqlSelect.setStringValue(this.sqlSelect.getStringValue() 
                        + Constants.COMMA + TableUtils.sqlSelect(clazz, columnsToString2(false, columns)));
            }
        }
        return typedThis;
    }

    @SafeVarargs
    @Override
    public final LambdaFindWrapper<T> selectAlias(String alias, SFunction<T, ?>... columns) {
        if (ArrayUtils.isNotEmpty(columns)) {
            if (StringUtils.isBlank(this.sqlSelect.getStringValue())) {
                this.sqlSelect.setStringValue(TableUtils.sqlSelect(alias, columnsToString2(false, columns))); 
            } else {
                this.sqlSelect.setStringValue(this.sqlSelect.getStringValue() 
                        + Constants.COMMA + TableUtils.sqlSelect(alias, columnsToString2(false, columns)));
            }
        }
        return typedThis;
    }

    @SafeVarargs
    protected final String[] columnsToString2(boolean onlyColumn, SFunction<T, ?>... columns) {
        List<String> list = Arrays.stream(columns).map(i -> columnToString(i, onlyColumn)).collect(Collectors.toList());
        return list.toArray(new String[list.size()]);
    }
    
    /**
     * SELECT 部分 SQL 设置
     *
     * @param columns 查询字段
     */
    @SafeVarargs
    @Override
    public final LambdaFindWrapper<T> selectOrig(SFunction<T, ?>... columns) {
        if (ArrayUtils.isNotEmpty(columns)) {
            this.sqlSelect.setStringValue(columnsToString(false, columns));
        }
        return typedThis;
    }

    /**
     * 过滤查询的字段信息(主键除外!)
     * <p>
     * 例1: 只要 java 字段名以 "test" 开头的 -> select(i -&gt;
     * i.getProperty().startsWith("test"))
     * </p>
     * <p>
     * 例2: 只要 java 字段属性是 CharSequence 类型的 ->
     * select(TableFieldInfo::isCharSequence)
     * </p>
     * <p>
     * 例3: 只要 java 字段没有填充策略的 -> select(i -&gt; i.getFieldFill() ==
     * FieldFill.DEFAULT)
     * </p>
     * <p>
     * 例4: 要全部字段 -> select(i -&gt; true)
     * </p>
     * <p>
     * 例5: 只要主键字段 -> select(i -&gt; false)
     * </p>
     *
     * @param predicate 过滤方式
     * @return this
     */
    @Override
    public LambdaFindWrapper<T> selectOrig(Class<T> entityClass, Predicate<TableFieldInfo> predicate) {
        if (entityClass == null) {
            entityClass = getEntityClass();
        } else {
            setEntityClass(entityClass);
        }
        Assert.notNull(entityClass, "entityClass can not be null");
        this.sqlSelect
                .setStringValue(TableInfoHelper.getTableInfo(entityClass).chooseSelect(predicate));
        return typedThis;
    }

    @Override
    public String getSqlSelect() {
        return sqlSelect.getStringValue();
    }

    /**
     * 用于生成嵌套 sql
     * <p>故 sqlSelect 不向下传递</p>
     */
    @Override
    protected LambdaFindWrapper<T> instance() {
        return new LambdaFindWrapper<>(getEntity(), getEntityClass(), null, paramNameSeq, paramNameValuePairs,
            new MergeSegments(), paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());
    }

    @Override
    public void clear() {
        super.clear();
        sqlSelect.toNull();
    }
}
